/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package it.tukano.swingdeco.containers;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.util.ArrayList;

/**
 * A layout manager that displays components in a wrapped line.
 * @author pgi
 */
public class DynamicGridLayout implements LayoutManager {
    private int margin;

    /**
     * Instance initializer, set the margin to 0
     */
    public DynamicGridLayout() {}

    /**
     * Initializes this layout with the given margin
     * @param margin the space between adjacent components
     */
    public DynamicGridLayout(int margin) {
        this.margin = margin;
    }

    /**
     * Unused
     */
    public void addLayoutComponent(String name, Component comp) {

    }

    /**
     * Unused
     */
    public void removeLayoutComponent(Component comp) {

    }

    /**
     * Computes the preferred size of the given container
     * @param parent the container that uses this layout manager
     * @return the preferred size of the component.
     */
    public Dimension preferredLayoutSize(Container parent) {
        synchronized (parent.getTreeLock()) {
            return computePreferredLayoutSize(parent);
        }
    }

    /**
     * Same as preferred layout size
     * @param parent the container
     * @return the preferred layout size
     */
    public Dimension minimumLayoutSize(Container parent) {
        return preferredLayoutSize(parent);
    }

    /**
     * Sets the bounds of the components in the given container using the logic
     * of this layout manager
     * @param parent the container to layout
     */
    public void layoutContainer(Container parent) {
        synchronized (parent.getTreeLock()) {
            doLayoutContainer(parent);
        }
    }

    private Component[] getVisibleComponents(Container parent) {
        ArrayList<Component> components = new ArrayList<Component>();
        for (Component component : parent.getComponents()) {
            if(component.isVisible()) components.add(component);
        }
        return components.toArray(new Component[components.size()]);
    }

    private void doLayoutContainer(Container parent) {
        Component[] components = getVisibleComponents(parent);
        int componentCount = components.length;
        if(componentCount > 0) {
            int containerWidth = parent.getWidth();
            int containerHeight = parent.getHeight();
            Dimension cellSize = computeBiggestComponentSize(parent);
            Insets parentInsets = parent.getInsets();
            int x0 = parentInsets.left;
            int y0 = parentInsets.right;
            int availableWidth = containerWidth - parentInsets.left - parentInsets.right;
            Rectangle bounds = new Rectangle(x0, y0, cellSize.width, cellSize.height);
            int columnCount = Math.max(1, availableWidth / (cellSize.width + getMargin()));
            int rowCount = 1 + componentCount / columnCount;
            for(int i = 0; i < componentCount; i++) {
                Component component = components[i];
                int row = i / columnCount;
                int col = i % columnCount;
                bounds.x = x0 + col * (cellSize.width + getMargin());
                bounds.y = y0 + row * (cellSize.height + getMargin());
                component.setBounds(bounds);
            }
        }
    }

    private int getVisibleComponentsCount(Container parent) {
        int count = 0;
        Component[] components = parent.getComponents();
        for (int i = 0; i < components.length; i++) {
            Component component = components[i];
            if(component.isVisible()) count++;
        }
        return count;
    }

    private Dimension computePreferredLayoutSize(Container parent) {
        Dimension size = new Dimension();
        Insets parentInsets = parent.getInsets();
        int componentCount = getVisibleComponentsCount(parent);
        if(componentCount > 0) {
            int containerWidth = parent.getWidth() - parentInsets.left - parentInsets.right;
            Dimension biggestComponentSize = computeBiggestComponentSize(parent);
            biggestComponentSize.width += getMargin();
            biggestComponentSize.height += getMargin();
            int columnCount = Math.max(1, containerWidth / biggestComponentSize.width);
            int rowCount = componentCount / columnCount;
            if(componentCount % columnCount != 0) rowCount += 1;
            size.width = biggestComponentSize.width * columnCount;
            size.height = biggestComponentSize.height * rowCount;
        }
        size.width += parentInsets.left + parentInsets.right;
        size.height += parentInsets.top + parentInsets.bottom;
        return size;
    }

    private int getMargin() {
        return margin;
    }

    private Dimension computeBiggestComponentSize(Container parent) {
        Dimension size = new Dimension();
        Component[] components = getVisibleComponents(parent);
        int componentCount = components.length;
        for(int i = 0; i < componentCount; i++) {
            Component component = components[i];
            Dimension componentPreferredSize = component.getPreferredSize();
            size.width = Math.max(size.width, componentPreferredSize.width);
            size.height = Math.max(size.height, componentPreferredSize.height);
        }
        return size;
    }
}
